package com.alog.storage.net;

import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.alog.storage.domain.ResponseInfo;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * 网络请求类
 */
public class Connector {
    public final static int BODY_TYPE_DEFAULT = 0;
    public final static int BODY_TYPE_JSON = 1;
    public final static int BODY_TYPE_XML = 2;
    private final MediaType MEDIA_TYPE_DEFAULT = MediaType.parse("application/octet-stream; charset=utf-8");
    private final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
    private final MediaType MEDIA_TYPE_XML = MediaType.parse("text/xml; charset=utf-8");
    private final int FAILURE = -1;
    private final int SUCCESS = 0;
    private final String TYPE_ENCRYPT = "secure/protobuf";
    private final int TIMEOUT = 10 * 1000;
    private final int CACHE_SIZE = 10 * 1024 * 1024;
    private OkHttpClient mOkHttpClient;
    private static Connector mConnector;

    private Connector(Context context) {
        try {
            SSLSocketFactory[] socketFactory = new SSLSocketFactory[1];
            X509TrustManager[] trustManager = new X509TrustManager[1];
            SslSocketFactory.getSocketFactory(socketFactory, trustManager/*, context.getAssets().open("server.cer")*/);
            Cache cache = new Cache(getCacheDir(context), CACHE_SIZE);
            mOkHttpClient = new OkHttpClient.Builder().connectTimeout(TIMEOUT, TimeUnit.SECONDS).sslSocketFactory(socketFactory[0], trustManager[0]).cache(cache).build();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private File getCacheDir(Context context) {
        String cacheDir = null;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            cacheDir = context.getExternalCacheDir().getPath();
        } else {
            cacheDir = context.getCacheDir().getPath();
        }
        return new File(cacheDir);
    }

    public static Connector getConnector(Context context) {
        if (mConnector == null) {
            mConnector = new Connector(context);
        }
        return mConnector;
    }

    /**
     * get方式提交数据
     *
     * @param apiId    接口编号
     * @param url      接口url
     * @param headers  头字段
     * @param listener 回调引用
     */
    public synchronized void doGet(int apiId, String url, HashMap<String, String> headers, RequestCallback listener) {
        Request.Builder requestBuilder = new Request.Builder();
        requestBuilder.tag(listener.getClass().getSimpleName());
        requestBuilder.url(url);
        addHeader(requestBuilder, headers);
        mOkHttpClient.newCall(requestBuilder.build()).enqueue(new HttpCallback(apiId, listener));
    }

    /**
     * post方式提交数据
     *
     * @param apiId    接口编号
     * @param url      接口url
     * @param headers  头字段
     * @param bodyType 数据类型
     * @param body     数据
     * @param listener 回调引用
     */
    public synchronized void doPost(int apiId, String url, HashMap<String, String> headers, int bodyType, byte[] body, RequestCallback listener) {
        Request.Builder requestBuilder = new Request.Builder();
        requestBuilder.tag(listener.getClass().getSimpleName());
        requestBuilder.url(url);
        addHeader(requestBuilder, headers);
        requestBuilder.post(RequestBody.create(getMediaType(bodyType), body));
        mOkHttpClient.newCall(requestBuilder.build()).enqueue(new HttpCallback(apiId, listener));
    }

    /**
     * 上传文件
     *
     * @param apiId    接口编号
     * @param url      接口url
     * @param headers  头字段
     * @param file     文件
     * @param listener 回调引用
     */
    public synchronized void doPostFile(int apiId, String url, HashMap<String, String> headers, File file, RequestCallback listener) {
        Request.Builder requestBuilder = new Request.Builder();
        requestBuilder.tag(listener.getClass().getSimpleName());
        requestBuilder.url(url);
        addHeader(requestBuilder, headers);
        requestBuilder.post(RequestBody.create(getMediaType(BODY_TYPE_DEFAULT), file));
        mOkHttpClient.newCall(requestBuilder.build()).enqueue(new HttpCallback(apiId, listener));
    }

    private void addHeader(Request.Builder requestBuilder, HashMap<String, String> headers) {
        if (headers != null && !headers.isEmpty()) {
            Iterator itr = headers.keySet().iterator();
            String key = null;
            while (itr.hasNext()) {
                key = itr.next().toString();
                requestBuilder.addHeader(key, headers.get(key));
            }
        }
    }

    private MediaType getMediaType(int bodyType) {
        switch (bodyType) {
            case BODY_TYPE_JSON:
                return MEDIA_TYPE_JSON;
            case BODY_TYPE_XML:
                return MEDIA_TYPE_XML;
        }
        return MEDIA_TYPE_DEFAULT;
    }

    /**
     * 取消任务
     *
     * @param tag 任务名称
     */
    public synchronized void cancel(String tag) {
        if (TextUtils.isEmpty(tag)) {
            return;
        }
        cancelCall(mOkHttpClient.dispatcher().queuedCalls(), tag);
        cancelCall(mOkHttpClient.dispatcher().runningCalls(), tag);
    }

    private void cancelCall(List<Call> calls, String cancelTag) {
        if (calls.size() > 0) {
            for (Call call : calls) {
                if (call.request().tag() != null && call.request().tag().toString().equalsIgnoreCase(cancelTag)) {
                    call.cancel();
                }
            }
        }
    }

    private class HttpCallback implements Callback {
        private int apiId;
        private RequestCallback listener;

        public HttpCallback(int apiId, RequestCallback listener) {
            this.apiId = apiId;
            this.listener = listener;
        }

        @Override
        public void onFailure(Call call, IOException e) {
            if (call.isCanceled()) {
                return;
            }
            if (listener != null) {
                Message msg = mHandler.obtainMessage();
                msg.what = FAILURE;
                msg.obj = listener;
                msg.arg1 = apiId;
                mHandler.sendMessage(msg);
            }
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (call.isCanceled()) {
                return;
            }
            if (listener != null) {
                String contentTypeValue = response.headers().get("Content-Type");
                boolean isEncrypt = false;
                if (contentTypeValue != null && TYPE_ENCRYPT.equalsIgnoreCase(contentTypeValue)) {
                    isEncrypt = true;
                }
                Message msg = mHandler.obtainMessage();
                msg.what = SUCCESS;
                Bundle b = new Bundle();
                b.putSerializable("listener", listener);
                b.putSerializable("responseInfo", new ResponseInfo(apiId, isEncrypt, response.body().bytes()));
                msg.setData(b);
                mHandler.sendMessage(msg);
            }
        }
    }

    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case SUCCESS:
                    Bundle b = msg.getData();
                    RequestCallback listener = (RequestCallback) b.getSerializable("listener");
                    listener.onResponse((ResponseInfo) b.getSerializable("responseInfo"));
                    break;
                case FAILURE:
                    listener = (RequestCallback) msg.obj;
                    listener.onErrorResponse(msg.arg1);
                    break;
            }
        }
    };

    public interface RequestCallback extends Serializable {
        public void onResponse(ResponseInfo response);

        public void onErrorResponse(int apiId);
    }

}
